Skip to content

Refactor Rosenbrock23/32 to use RodasTableau#3273

Open
singhharsh1708 wants to merge 6 commits intoSciML:masterfrom
singhharsh1708:rosenbrock-tableau-refactor
Open

Refactor Rosenbrock23/32 to use RodasTableau#3273
singhharsh1708 wants to merge 6 commits intoSciML:masterfrom
singhharsh1708:rosenbrock-tableau-refactor

Conversation

@singhharsh1708
Copy link
Copy Markdown
Contributor

@singhharsh1708 singhharsh1708 commented Mar 29, 2026

Summary

This PR migrates Rosenbrock23 and Rosenbrock32 to the existing RodasTableau-based generic Rosenbrock implementation and removes their per-method implementations.

Migration (net -849 LOC)

  • Added Rosenbrock23RodasTableau and Rosenbrock32RodasTableau, converting Shampine coefficients to Rodas form via the identity J = W + M/(γdt)

  • Routed both methods through the generic perform_step!

  • Removed:

    • 4 per-method cache structs and corresponding alg_cache methods
    • 4 per-method perform_step! implementations
    • 2 initialize! methods
    • 2 legacy tableau structs
  • Removed Shampine-specific interpolation code (_ode_addsteps!, interp_summary, etc.)

Impact

  • ~849 LOC of solver-specific code removed
  • Both methods now share the same execution path as other Rosenbrock solvers
  • Simplifies maintenance and reduces duplication

Checklist


Additional context

This PR focuses on consolidating Rosenbrock23 and Rosenbrock32 into the existing tableau-based infrastructure. Any improvements to interpolation or dense output behavior can be addressed separately.

@singhharsh1708 singhharsh1708 changed the title Refactor Rosenbrock23/32 to RodasTableau + Fix Dense Output Interpolation Refactor Rosenbrock23/32 to use RodasTableau Mar 29, 2026
@singhharsh1708
Copy link
Copy Markdown
Contributor Author

refactor(Rosenbrock): migrate Rosenbrock23/32 to RodasTableau and remove per-method implementations

Comment on lines +4 to +10
# Original Shampine formulation uses scaled stages k̃ᵢ with J*k coupling.
# Converted to Rodas form (C/dt coupling, no explicit J*k) via the identity
# J = W + M/(γdt)
# which absorbs the Jacobian coupling into the LHS operator.
#
# Stage variable relationship: k̃ᵢ_old = Σⱼ L[i,j]*ks[j]/(dt*γ)
# where L = [1 0 0; 1 1 0; 2 c₃₂ 1].
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the performance impact? Show some stiff work precision diagrams

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i ran work-precision comparisons on several stiff and non-stiff problems to check for any performance impact.

  • Rosenbrock23: results are bit-identical to master across all tested problems
  • Rosenbrock32: shows improved accuracy (≈7–12× lower error)
  • Timing:
    • Slight overhead on trivial scalar problems (a few μs, dominated by dispatch)
    • Comparable performance on larger problems
    • No measurable difference on stiff problems (e.g., ROBER)
      Overall, there is no performance regression on realistic workloads, and behavior is preserved (or improved for Rosenbrock32).

@singhharsh1708
Copy link
Copy Markdown
Contributor Author

image

@ChrisRackauckas
Copy link
Copy Markdown
Member

What's the before and after, ROBER and Pollu

@ChrisRackauckas
Copy link
Copy Markdown
Member

okay looking good performance wise, but test failures and look into if the interpolant is done correctly

# No H matrix: set k[1]=f(uprev), k[2]=f(u_new) for Hermite interpolation
integrator.k[1] = fsalfirst_cache
integrator.k[2] = f(u, p, t + dt)
# No H matrix: compute Hermite interpolation coefficients
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is unrelated to the PR

@singhharsh1708 singhharsh1708 force-pushed the rosenbrock-tableau-refactor branch 2 times, most recently from aea607a to 9eb0a25 Compare March 30, 2026 12:49
@singhharsh1708
Copy link
Copy Markdown
Contributor Author

@ChrisRackauckas Local validation:

  • Rosenbrock23 error ~3e-4, Rosenbrock32 error ~1e-6
  • AD gradients consistent
  • Resize + solve passes across configurations

Rosenbrock32Cache,
},
}
return dense ? "specialized 2nd order \"free\" stiffness-aware interpolation" :
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why removed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those cache types (Rosenbrock23Cache, Rosenbrock32Cache, Rosenbrock23ConstantCache, Rosenbrock32ConstantCache) no longer exist — they've been replaced by the generic RosenbrockCache and RosenbrockConstantCache. So this method would never dispatch. The generic Rosenbrock interp_summary already covers RosenbrockCache/RosenbrockConstantCache.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then lower, the interpolation summary needs to get improved so it's based on the cache type's information in order to be correct? It should probably read the k-length in order to determine the order to share it, or have some other way to know it.

@singhharsh1708 singhharsh1708 force-pushed the rosenbrock-tableau-refactor branch from 9eb0a25 to 5d41711 Compare March 30, 2026 16:56
@ChrisRackauckas
Copy link
Copy Markdown
Member

test failures

@singhharsh1708 singhharsh1708 force-pushed the rosenbrock-tableau-refactor branch 2 times, most recently from 7635d01 to 87ea627 Compare March 31, 2026 21:46
@ChrisRackauckas
Copy link
Copy Markdown
Member

Rebase this: rosenbrock tests are passing and your test changes shouldn't be in the same PR.

Comment thread test/integrators/resize_tests.jl Outdated
@test length(i.cache.k₁) == 5
@test length(i.cache.k₂) == 5
@test length(i.cache.k₃) == 5
if hasfield(typeof(i.cache), :ks)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Comment thread test/regression/special_interps.jl Outdated
)
@test maximum(norm(soloop(t) - sol(t)) for t in 0:0.001:10) < 1.0e-10
@test maximum(norm(soloop(y1, t) - sol(y2, t)) for t in 0:0.001:10) < 1.0e-10
@test maximum(norm(soloop(t) - sol(t)) for t in 0:0.001:10) < 5.0e-10
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would this change?

@singhharsh1708 singhharsh1708 force-pushed the rosenbrock-tableau-refactor branch from 754fa35 to 0f954bc Compare April 1, 2026 13:59
@singhharsh1708
Copy link
Copy Markdown
Contributor Author

@ChrisRackauckas can you check once i reverted changes

@ChrisRackauckas
Copy link
Copy Markdown
Member

Rebase for the Hermite interpolation fixes

@singhharsh1708 singhharsh1708 force-pushed the rosenbrock-tableau-refactor branch 3 times, most recently from 5d41711 to e7881f7 Compare April 2, 2026 16:18
@singhharsh1708
Copy link
Copy Markdown
Contributor Author

Addressed review feedback and cleaned up the PR:

  • Removed incorrect Hermite interpolation changes and aligned with upstream behavior (store f₀, f₁ in k as expected by the generic Rosenbrock interpolant)
  • Ensured no interpolation or test-related changes remain in this PR (migration only)
  • Verified OOP/IIP paths produce consistent dense output (within machine precision)
  • Rebased onto latest master (includes upstream interpolation fixes)
    All remaining changes are strictly the Rosenbrock23/32 → RodasTableau migration.
    A separate PR handles the required test updates for the new RosenbrockCache structure.

@singhharsh1708 singhharsh1708 force-pushed the rosenbrock-tableau-refactor branch from cc57441 to 0d149da Compare April 7, 2026 20:11
@singhharsh1708
Copy link
Copy Markdown
Contributor Author

@ChrisRackauckas can you take a look at it once.

@singhharsh1708 singhharsh1708 force-pushed the rosenbrock-tableau-refactor branch from d696b75 to 066ef50 Compare April 8, 2026 06:15
@singhharsh1708 singhharsh1708 force-pushed the rosenbrock-tableau-refactor branch 3 times, most recently from acea03f to d491269 Compare April 12, 2026 17:13
@singhharsh1708
Copy link
Copy Markdown
Contributor Author

Rebased onto master (includes #3075); fixed strip_cache to include the new jac_reuse field.

@singhharsh1708
Copy link
Copy Markdown
Contributor Author

@ChrisRackauckas any reviews?

Comment on lines +148 to +153
if hasproperty(cache, :interp_order) && cache.interp_order == -1
# Hermite interpolation: k[1]=f₀, k[2]=f₁
# dH/dt = 6Θ(1-Θ)(y₁-y₀)/dt + f₀(3Θ²-4Θ+1) + f₁(3Θ²-2Θ)
@.. 6 * Θ * (1 - Θ) * (y₁ - y₀) / dt +
k[1] * (3 * Θ^2 - 4 * Θ + 1) +
k[2] * (3 * Θ^2 - 2 * Θ)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this changed? Where is the second order tableau of Rosenbrock23's interpolation?

convert(T, igamma / 6),
]

H = zeros(T, 0, 3)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is hermite, not the special interpolation?

@ChrisRackauckas
Copy link
Copy Markdown
Member

Look up some of the old examples in issues used to demonstrate difficult interpolations for stiff problems that led to using the Rosenbrock special interpolation, and demonstrate that interpolation is still used and not hermite. It looks like you're falling back to hermite here.

@singhharsh1708 singhharsh1708 force-pushed the rosenbrock-tableau-refactor branch from d491269 to 7f6a1e8 Compare April 14, 2026 18:56
singhharsh1708 pushed a commit to singhharsh1708/OrdinaryDiffEq.jl that referenced this pull request Apr 14, 2026
Replace H=zeros(T,0,3) (Hermite fallback) with the 1x3 dense coefficient
matrix H=[0, -1/(gamma*(1-2*gamma)), 0] for both Rosenbrock23 and Rosenbrock32,
yielding interp_order=1 and one dense stage vector.

Add interp_order==1 branches to all eight _ode_interpolant/_ode_interpolant!
overloads (Val{0}/Val{1}, idxs::Nothing/idxs, returning/mutating), which
implement p(Theta)=(1-Theta)*y0+Theta*y1+Theta*(1-Theta)*K[1] and its
first derivative.

This algebraically recovers the original MATLAB ODE Suite Shampine
'free' 2nd-order stiff-aware dense output. Fixes regression noticed
by ChrisRackauckas in SciML#3273.
@singhharsh1708
Copy link
Copy Markdown
Contributor Author

Restored the Shampine Rosenbrock23/32 interpolation. The fix encodes the dense output as H = [0, -1/(γ(1-2γ)), 0] (1×3), giving interp_order=1. The new interpolant branch implements p(Θ) = (1-Θ)y₀ + Θy₁ + Θ(1-Θ)K[1], which algebraically recovers the original c1k̃₁ + c2k̃₂ stiff dense output from the MATLAB ODE Suite paper.

@ChrisRackauckas
Copy link
Copy Markdown
Member

That would make c2 0?

@ChrisRackauckas
Copy link
Copy Markdown
Member

And it's the wrong order should be 2

@singhharsh1708
Copy link
Copy Markdown
Contributor Author

Yes, K[2]=0 in the Rodas 2-vector formula — the Θ² contribution from Shampine's c2 is already absorbed into K[1] and through y₁. Changed to a 2×3 H matrix (second row zero) so interp_order=2 and the existing 2-vector code path handles it.

@singhharsh1708 singhharsh1708 force-pushed the rosenbrock-tableau-refactor branch 2 times, most recently from 7590f47 to 3759810 Compare April 15, 2026 20:42
@singhharsh1708
Copy link
Copy Markdown
Contributor Author

@ChrisRackauckas Addressed all review feedback:

Shampine interpolation restored — Rosenbrock23/32 now use a 2×3 H matrix (H[1,2] = -1/(γ(1-2γ)), second row zero) giving interp_order=2. The standard Rodas 2-vector formula p(Θ) = (1-Θ)y₀ + Θ(y₁ + (1-Θ)(K[1] + Θ·K[2])) with K[2]=0 algebraically recovers the MATLAB ODE Suite c₁k̃₁ + c₂k̃₂ dense output. No Hermite fallback.
Fixed OOP addsteps guard — the constant cache _ode_addsteps! guard was < size(H,1) which was always false for H_rows=0 methods; now correctly uses < 2 as minimum.
Added strip_cache override for RosenbrockCache (concrete-typed fields reject Nothing).
Updated resize test field names (k₁/k₂/k₃ → ks, f₁ → dense).
Remaining CI failures are pre-existing (ODEInterfaceRegression/lts, ModelingToolkit/1.10, DelayDiffEq).

@ChrisRackauckas
Copy link
Copy Markdown
Member

Convergence tests failing.

@singhharsh1708 singhharsh1708 force-pushed the rosenbrock-tableau-refactor branch 2 times, most recently from 046b666 to e9df50c Compare April 16, 2026 21:36
@singhharsh1708
Copy link
Copy Markdown
Contributor Author

@ChrisRackauckas can you check once i have checked locally its paassing?

@ChrisRackauckas
Copy link
Copy Markdown
Member

Harsh Singh and others added 6 commits April 20, 2026 02:12
… framework

Unify Rosenbrock23 and Rosenbrock32 with the existing generic Rosenbrock/Rodas
infrastructure by expressing their coefficients as RodasTableau entries. This
removes ~900 lines of duplicated cache structs, perform_step!, interpolation,
and addsteps code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@singhharsh1708 singhharsh1708 force-pushed the rosenbrock-tableau-refactor branch from e9df50c to 1e70c8b Compare April 19, 2026 20:52
@singhharsh1708
Copy link
Copy Markdown
Contributor Author

Yes, tested locally — both convergence and DDE tests pass:
Rosenbrock23: order ≈ 2.0 ✓, DelayDiffEq all 3/3 ✓
Rosenbrock32: order ≈ 3.0 ✓, DelayDiffEq all 3/3 ✓
Known issue (pre-existing, not from this PR): dae_rosenbrock_ad_tests.jl fails on Julia 1.10 LTS with an @inferred type mismatch on Rodas5P — this exists on master too and blocks the full test suite from reaching the convergence tests on LTS.
Setup difference: I tested on Julia 1.10 / macOS aarch64. The failing CI job ran on Julia 1.12.6 / x64 Linux (your machine). The DelayDiffEq failure may have been from an older commit — I've rebased onto latest master. Could you re-run to confirm? If it still fails on 1.12 Linux I'm happy to dig further, but I can't reproduce it locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants